#!/usr/bin/env python
#  -*- coding: utf-8 -*- 
__author__ = 'liu'

"""
公共库v16
修改记录:
1. 2022.7.16  增加了期货账号打头的json文件名用于区分
2. 2022.8.8   json文件名前缀增加策略名
3. 2022.9.6   区分整数和浮点数
4. 2022.10.12 入参filename合并
5. 2022.10.14 最小化引用
6. 2022.10.26 json key改为字符串
7. 2022.11.24 json文件集中存放到Jsons目录
8. 2023.3.16  增加读取策略参数配置文件功能
9. 2023.3.30  修改空Series类型告警
10. 2024.6.15 归一化获取整数和浮点数以及其他数据，而且可以自定义默认值
11. 2024.7.14 增加邮件预警接口
12. 2024.8.10 修改邮件发送接口，可发送多个地址；无配置直接退出；json读取接口去掉目录，改由调用方处理
13. 2024.8.23 区分邮件标题和正文
14. 2024.9.17 修改日志规则配置函数，修改日志文件名，增加输出到屏幕功能
15. 2024.9.18 修改日志规则配置函数，增加允许打印到屏幕的参数，默认关闭
16. 2024.9.24 优化json文件读写函数
17. 2024.10.1 修改日志规则配置函数，去掉允许打印到屏幕的参数，改为一直允许打印
18. 2024.10.22 将日志和json目录改为小写
19. 2025.2.25 增加清除json文件的函数
20. 2025.3.3 处理json文件中文乱码问题
21. 2025.3.7 增加判断字符串是否包含数字函数
22. 2025.3.9 增加截取排序后字典前后N个元素函数
"""
from os import makedirs
from os.path import exists
from re import search
from time import strftime, localtime, time
from logging import INFO, FileHandler, Formatter, StreamHandler
from json import load, dump
from pandas import Series, read_csv
from smtplib import SMTP_SSL
from email.mime.text import MIMEText
from email.header import Header

# 操作类别
OPER_NONE = 0
OPER_CANCEL_BUY_OPEN        = 0x1
OPER_CANCEL_SELL_OPEN       = 0x2
OPER_CANCEL_BUY_CLOSE       = 0x4
OPER_CANCEL_SELL_CLOSE      = 0x8
OPER_ORDER_BUY_OPEN         = 0x10
OPER_ORDER_SELL_OPEN        = 0x20
OPER_ORDER_BUY_CLOSE        = 0x40
OPER_ORDER_SELL_CLOSE       = 0x80
OPER_ORDER_BUY_OPEN_COMMIT  = 0x100
OPER_ORDER_SELL_OPEN_COMMIT = 0x200

# 获取日志文件全路径
def get_log_file_path(file_name):
    log_path = "./logs/"
    if not exists(log_path):
        makedirs(log_path)
    # 取日期做文件名
    rq = strftime('%Y%m%d', localtime(time()))
    log_file = log_path + rq + '_' + file_name + ".log"
    return log_file

# 设置日志规则
def set_log_rule(logger, log_file):
    # Log等级总开关
    logger.setLevel(INFO)

    # 定义handler的输出格式
    formatter = Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")

    # 创建一个Handler，用于写入日志文件
    fh = FileHandler(log_file)
    fh.setLevel(INFO)
    fh.setFormatter(formatter)
    logger.addHandler(fh)

    # 输出到控制台
    sh = StreamHandler()
    sh.setLevel(INFO)
    sh.setFormatter(formatter)
    logger.addHandler(sh)

# 获取Json文件全路径
def get_json_file_path(file_name):
    json_path = "./jsons/"
    if not exists(json_path):
        makedirs(json_path)
    json_file = json_path + file_name + '.json'
    return json_file

# 按key从json文件中获取数据，如果不存在，则返回默认值
def get_variable(json_file, key, default = None):
    # 文件不存在返回默认值
    if not exists(json_file):
        return default

    # 获取json数据，如异常返回默认值
    try:
        with open(json_file, "r", encoding='utf-8') as r:
            dt = load(r)
    except Exception as e:
        print(e)
        return default

    return dt.get(key, default)

# 按key保存json文件
def save_variable(json_file, key, value):
    dt = {}
    # 文件存在，读取原有数据
    if exists(json_file):
        try:
            with open(json_file, "r", encoding='utf-8') as r:
                dt = load(r)
        except Exception as e:
            print(e)

    # 更新字段的值
    dt[key] = value

    # 保存json文件
    try:
        with open(json_file, "w", encoding="utf-8") as w:
            dump(dt, w, ensure_ascii=False)
    except Exception as e:
        print(e)

# 清空json文件
def clear_json_file(json_file):
    dt = {}
    # 保存json文件
    try:
        with open(json_file, "w", encoding="utf-8") as w:
            dump(dt, w, ensure_ascii=False)
    except Exception as e:
        print(e)

# 按索引读取配置文件
def get_config(filename, index):
    ser = Series([], dtype = 'float64')
    df = read_csv(filename, header=None)
    df_len = len(df)
    if (index >= 1 and index <= df_len):
        ser = df.iloc[index - 1]
    return ser

# 发送邮件提醒
def send_email(mail_addr, auth_code, mail_to, mail_header, mail_text):
    # 未配置邮件直接返回
    if (mail_addr == '' or auth_code =='' or mail_to ==''):
        return

    # 创建邮件对象
    message = MIMEText(mail_text, 'plain', 'utf-8')
    message['From'] = Header(mail_addr + ' <' + mail_addr + '>')
    message['To'] = Header('All', 'utf-8')
    message['Subject'] = Header(mail_header, 'utf-8')

    # 登录SMTP服务器，发送邮件
    server = SMTP_SSL('smtp.' + mail_addr.split('@')[1], 465)
    try:
        server.login(mail_addr, auth_code)
        mail_to_format = mail_to.split(',')
        server.sendmail(mail_addr, mail_to_format, message.as_string())
        server.quit()  # 关闭连接
    except Exception as mail_error:
        print(mail_error)

# 字符串是否包含数字
def contains_digit(s):
    # 使用正则表达式匹配字符串中的数字
    return bool(search(r'\d', s))

# 截取字典的前N个和后N个元素，合并成新字典（如有重叠，返回原字典）
def slice_and_merge_dict(input_dict, n):
    # 获取字典总长度
    total_length = len(input_dict)
    
    # 如果n <= 0，直接返回原字典
    if n <= 0:
        return input_dict
    
    # 如果2n >= 总长度，说明会重叠，直接返回原字典
    if 2 * n >= total_length:
        return input_dict
    
    # 将字典转换为列表形式以保持顺序
    items = list(input_dict.items())
    
    # 截取前n个元素
    front_items = items[:n]
    # 截取后n个元素
    back_items = items[-n:]
    
    # 创建新字典，先放入前n个，再放入后n个
    result = dict(front_items + back_items)
    
    return result
